package com.zhouixi.reproduce.utils;

import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Component;

import com.mysql.jdbc.Constants;

import cn.hutool.json.JSON;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class DistributedLockUtil {

	 
	@Resource
    private  RedisTemplate<String, Object> redisTemplate;

	/**
	 * 加锁默认超时时间
	 */
	private static final long DEFAULT_TIMEOUT_SECOND = 5;

	/**
	 * 加锁循环等待时间
	 */
	private static final long LOOP_WAIT_TIME_MILLISECOND = 30;

	/**
	 * 加锁
	 * 
	 * @param key
	 * @param timeoutSecond 如果为null,使用默认超时时间
	 * @return 加锁的值（超时时间）
	 */
	public boolean lock(String key, Long timeoutSecond) {

		log.info("Thread：" + Thread.currentThread().getName() + " start lock");

		// 如果参数错误
		if (timeoutSecond != null && timeoutSecond <= 0) {
			timeoutSecond = DEFAULT_TIMEOUT_SECOND;
		}
		final long newtimeoutSecond = timeoutSecond == null ? DEFAULT_TIMEOUT_SECOND : timeoutSecond;

		while (true) {

			// 超时时间点
			long timeoutTimeMilli = currentTimeMilliForRedis() + timeoutSecond * 1000;
			System.out.println(timeoutTimeMilli);
			// 如果设置成功
			if (true) {
				SessionCallback<Boolean> sessionCallback = new SessionCallback<Boolean>() {
		            List<Object> exec = null;
		            @Override
		            @SuppressWarnings("unchecked")
		            public Boolean execute(RedisOperations operations) throws DataAccessException {
		            	
		                operations.multi();
		                redisTemplate.opsForValue().setIfAbsent(key,Thread.currentThread().getId());
		                redisTemplate.expire(key,newtimeoutSecond, TimeUnit.SECONDS);
		                exec = operations.exec();
		                if(exec.size() > 0) {
		                	System.out.println(Thread.currentThread().getName());
		                	
		                	if((Boolean)exec.get(0)) {
		                		log.info("Thread：" + Thread.currentThread().getName() + " lock success");
		                	}
		                    return (Boolean) exec.get(0);
		              
		                }
		                return false;
		            }
		        };
		        return redisTemplate.execute(sessionCallback);
			}

			// 如果已经超时
			Long value = (Long) redisTemplate.opsForValue().get(key);
			if (value != null && value.longValue() < currentTimeMilliForRedis()) {

				// 设置新的超时时间
				Long oldValue = (Long) redisTemplate.opsForValue().getAndSet(key, timeoutTimeMilli);// 旧的值

				// 多个线程同时getset，只有第一个才可以获取到锁
				if (value.equals(oldValue)) {
					log.info("Thread：" + Thread.currentThread().getName() + " lock success");
					return false;
				}
			}

			// 延迟一定毫秒，防止请求太频繁
			try {
				Thread.sleep(LOOP_WAIT_TIME_MILLISECOND);
			} catch (InterruptedException e) {
				log.error("DistributedLockUtil lock sleep error", e);
			}
		}
	}

	/**
	 * 释放锁
	 * 
	 * @param key
	 * @param lockValue
	 */
	public  void unLock(String key, long lockValue) {

		log.info("Thread：" + Thread.currentThread().getName() + " start unlock");

		Long value = (Long) redisTemplate.opsForValue().get(key);
		if (value != null && value.equals(lockValue)) {// 如果是本线程加锁
			redisTemplate.delete(key);
			log.info("Thread：" + Thread.currentThread().getName() + " unlock success");
		}
	}

	/**
	 * redis服务器时间
	 * 
	 * @return
	 */
	private  long currentTimeMilliForRedis() {

		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.time();
			}
		});
	}
}
